package com.ctibook.booksystem.dao.bookcatalog.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermEnum;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.hibernate.loader.custom.Return;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.SearchFactory;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.springframework.stereotype.Repository;

import com.cti.ftpservice.dao.impl.FullTextSearchDao;
import com.cti.ftpservice.ftp.DateHelper;
import com.cti.ftpservice.utils.IsbnConvertUtils;
import com.cti.ftpservice.vo.PageViewVO;
import com.ctibook.booksystem.dao.bookcatalog.BookInfoFullTextSearchDao;
import com.ctibook.booksystem.model.bookcatalog.BookInfo;
import com.ctibook.booksystem.model.bookcatalog.BookInfoExtral;
import com.ctibook.booksystem.vo.bookcatalog.BookInfoSearchNormalVO;
import com.ctibook.booksystem.vo.bookcatalog.BookInfoSearchWebVO;
import com.ctibook.booksystem.vo.bookcatalog.BookInfoStatisticsVo;
import com.ctibook.booksystem.vo.bookcatalog.BookInfoStatisticsTJVo;


/**
 * @author 鲁永刚 E-mail: luyg@ctibook.com
 * @version 创建时间：Sep 16, 2014 10:27:57 AM 类说明
 */
@Repository
public class BookInfoFullTextSearchDaoImpl extends FullTextSearchDao implements BookInfoFullTextSearchDao {

	private static Calendar minCalendar;
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

	static {
		minCalendar = Calendar.getInstance();
		minCalendar.set(Calendar.YEAR, 1900);
		minCalendar.set(Calendar.MONTH, 0);
		minCalendar.set(Calendar.DATE, 1);
	}

	@Override
	public PageViewVO<BookInfo> getBook(int pageIndex, int pageSize) {
		return searchByBlank(BookInfo.class, pageIndex, pageSize, BookInfo.class);
	}

	@Override
	public PageViewVO<BookInfo> getBookByAnyProp(String searchText, int pageIndex, int pageSize) {
		QueryBuilder queryBuilder = getQueryBuilder(BookInfo.class);
		BooleanQuery booleanQuery = new BooleanQuery();
		Query query = null;
		// isbn
		query = queryBuilder.keyword().wildcard().onField("isbn").matching(searchText).createQuery();
		booleanQuery.add(query, Occur.SHOULD);
		// title
		query = queryBuilder.keyword().onField("title").matching(searchText).createQuery();
		booleanQuery.add(query, Occur.SHOULD);
		// chineseTitle
		query = queryBuilder.keyword().onField("chineseTitle").matching(searchText).createQuery();
		booleanQuery.add(query, Occur.SHOULD);
		// publisher.name
		query = queryBuilder.keyword().onField("publisher.name").matching(searchText).createQuery();
		booleanQuery.add(query, Occur.SHOULD);
		// subjects.name
		query = queryBuilder.keyword().onField("subjects.name").matching(searchText).createQuery();
		booleanQuery.add(query, Occur.SHOULD);
		// clcs.clc
		query = new PrefixQuery(new Term("clcs.clc", searchText));
		booleanQuery.add(query, Occur.SHOULD);
		// author
		query = queryBuilder.keyword().onField("author").matching(searchText).createQuery();
		booleanQuery.add(query, Occur.SHOULD);
		// description
		query = queryBuilder.keyword().onField("description").matching(searchText).createQuery();
		PageViewVO<BookInfo> pg = executeQuery(booleanQuery, pageIndex, pageSize, BookInfo.class);
		return pg;
	}

	@Override
	public PageViewVO<BookInfo> getBookBySingleProp(String propName, Object searchValue, int pageIndex, int pageSize) {
		if (propName == null || propName.trim().isEmpty())
			return searchByBlank(BookInfo.class, pageIndex, pageSize, BookInfo.class);
		if (propName.equalsIgnoreCase("isbn")) {
			return searchByWildcard(BookInfo.class, propName, searchValue.toString().toLowerCase(), pageIndex, pageSize, BookInfo.class);
		} else if (propName.equalsIgnoreCase("clcs.clc")) {
			return searchByPrefix(BookInfo.class, propName, searchValue.toString().toLowerCase(), pageIndex, pageSize, BookInfo.class);
		} else {
			return searchByNormal(BookInfo.class, propName, searchValue.toString().toLowerCase(), pageIndex, pageSize, BookInfo.class);
		}
	}

	@Override
	public PageViewVO<BookInfo> getBooksByIsbns(List<String> isbns, int pageIndex, int pageSize) {
		if (isbns == null || isbns.size() == 0)
			return getBook(pageIndex, pageSize);
		String queryStr = getQueryStringByStringList(isbns);
		PageViewVO<BookInfo> pg = searchByQueryString(BookInfo.class, queryStr, pageIndex, pageSize, BookInfo.class);
		return pg;
	}

	@Override
	public List<String> getNotExistIsbnsByIsbns(List<String> isbns) {
		if (isbns == null || isbns.size() == 0)
			return new ArrayList<String>();
		String queryStr = getQueryStringByStringList(isbns);
		List<Object> existObjs = searchByQueryStringAndProjectionProp(BookInfo.class, queryStr, new String[] { "isbn" }, BookInfo.class);
		List<String> existIsbns = new ArrayList<String>();
		for (Object obj : existObjs) {
			String isbn = (String) ((Object[]) obj)[0];
			existIsbns.add(isbn);
		}
		List<String> noExistIsbns = new ArrayList<String>();
		for (String isbn : isbns) {
			if (!existIsbns.contains(isbn)) {
				noExistIsbns.add(isbn);
			}
		}
		return noExistIsbns;
	}

	@Override
	public void rebuildIndex() {
		rebuildIndex(BookInfo.class);
	}

	public void rebuildIndexBookInfoExtral() {
		rebuildIndex(BookInfoExtral.class);
	}

	@Override
	public PageViewVO<BookInfo> getBookByKeyWordAnalyzer(String propName, String propValue, int pageIndex, int pageSize) {
		if (propValue == null || propValue.isEmpty()) {
			QueryBuilder queryBuilder = getQueryBuilder(BookInfo.class);
			Query query = queryBuilder.keyword().wildcard().onField(propName).matching("*").createQuery();
			return executeQuery(query, pageIndex, pageSize, BookInfo.class);
		}
		String searchText = propName + ":" + propValue;
		Analyzer analyzer = new KeywordAnalyzer();
		QueryParser queryParser = new QueryParser(getCurrentLuceneVersion(), "id", analyzer);
		try {
			Query query = queryParser.parse(searchText);
			return executeQuery(query, analyzer, pageIndex, pageSize, BookInfo.class);
		} catch (ParseException e) {
			throw new RuntimeException(e);
		}

	}

	@Override
	public PageViewVO<BookInfo> getBooksPageViewByQueryTextAndAnalyzer(String queryText, Analyzer analyzer, int pageIndex, int pageSize) {
		QueryParser queryParser = new QueryParser(getCurrentLuceneVersion(), "id", analyzer);
		Query query;
		try {
			query = queryParser.parse(queryText);
			return executeQuery(query, pageIndex, pageSize, BookInfo.class);
		} catch (ParseException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("rawtypes")
	@Override
	public PageViewVO<BookInfo> getBookByMultiProps(Set<String> propNames, String keyword, String mustPropName, boolean mustPropValue, int pageIndex, int pageSize) {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		boolQr.must(qr.keyword().onField(mustPropName).matching(mustPropValue).createQuery());
		BooleanJunction<BooleanJunction> shouldQr = qr.bool();
		for (String propName : propNames) {
			shouldQr.should(qr.keyword().onField(propName).matching(keyword).createQuery());
		}
		Query query = boolQr.must(shouldQr.createQuery()).createQuery();
		return executeQuery(query, pageIndex, pageSize, BookInfo.class);
	}

	@Override
	public List<String> getDistinctListByField(String field) {
		String internedFieldName = field.intern();
		SearchFactory searchFactory = getFullTextSession().getSearchFactory();
		IndexReader indexReader = searchFactory.getIndexReaderAccessor().open(BookInfo.class);
		List<String> list = new ArrayList<String>();

		try {
			TermEnum termEnum = indexReader.terms();
			while (termEnum.next()) {
				Term term = termEnum.term();
				if (internedFieldName != term.field()) {
					continue;
				}
				String text = term.text();
				list.add(text);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		}

		return list;
	}

	@SuppressWarnings({ "rawtypes", "static-access"})
	@Override
	public PageViewVO<BookInfo> getBookInfoByNormalSearchVO(BookInfoSearchNormalVO vo, int pageIndex, int pageSize) {
		Query query=getBookInfoByNormalSearchVOQuery(vo);
		Sort sort;
		if(vo.getOrdeByProperties()!=null&&vo.getOrdeByProperties().length>0){
			SortField[] sortFields=new SortField[vo.getOrdeByProperties().length];
			for (int i = 0; i < vo.getOrdeByProperties().length; i++) {
				String[] props=vo.getOrdeByProperties()[i].split(",");
				if(props[0].equals("price")){
					if(props[1].equals("asc"))
						sortFields[i]=new SortField(props[0],SortField.FLOAT,false);
					else
						sortFields[i]=new SortField(props[0],SortField.FLOAT,true);
				}else if(props[0].equals("stockCount")||props[0].equals("pageSize")){
					if(props[1].equals("asc"))
						sortFields[i]=new SortField(props[0],SortField.INT,false);
					else
						sortFields[i]=new SortField(props[0],SortField.INT,true);
				}else{
					if(props[1].equals("asc"))
						sortFields[i]=new SortField(props[0],SortField.STRING,false);
					else
						sortFields[i]=new SortField(props[0],SortField.STRING,true);
				}
			}	
			sort=new Sort(sortFields);
		}else{
			sort=new Sort(new SortField("pubDate",SortField.STRING,true));
		}
		
		return executeQuery(query,sort, pageIndex, pageSize, BookInfo.class);
	}
	
	private Query getBookInfoByNormalSearchVOQuery(BookInfoSearchNormalVO vo){
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		//boolQr.must(qr.keyword().onField("isDel").matching(false).createQuery());
		//boolQr.must(qr.keyword().onField("isWaitForHumanCheckApprove").matching(false).createQuery());
		boolQr.must(qr.keyword().onField("isBlackBook").matching(false).createQuery());
		if (vo.getIsbns() != null && vo.getIsbns().size() > 0) {
			if(vo.getIsbns().size()>=1 && vo.getIsbns().size()<=1000){
				BooleanJunction<BooleanJunction> isbnBoolQr = qr.bool();
				for (String isbn : vo.getIsbns()) {
					String standardIsbn = isbn.toLowerCase();
					if (IsbnConvertUtils.validate(standardIsbn)) {
						standardIsbn = IsbnConvertUtils.convertTo13place(standardIsbn);
					}
					standardIsbn = standardIsbn.toLowerCase();
					isbnBoolQr.should(qr.keyword().wildcard().onField("isbn").matching(standardIsbn).createQuery());
				}
				boolQr.must(isbnBoolQr.createQuery());
			}else{
				BooleanQuery booleanQuery = new BooleanQuery();
				booleanQuery.setMaxClauseCount(vo.getIsbns().size()+100);
				//booleanQuery.add(qr.keyword().onField("isDel").matching(false).createQuery(), Occur.MUST);
				//booleanQuery.add(qr.keyword().onField("isWaitForHumanCheckApprove").matching(false).createQuery(), Occur.MUST);
				booleanQuery.add(qr.keyword().onField("isBlackBook").matching(false).createQuery(), Occur.MUST);
				/*if(StringUtils.isNoneBlank(vo.getCheckStatus())){
					booleanQuery.add(qr.keyword().onField("checkStatus").matching(vo.getCheckStatus()).createQuery(), Occur.MUST);
				}*/
				BooleanQuery booleanQueryIsbns = new BooleanQuery();
				for (String isbn : vo.getIsbns()) {
					String standardIsbn = isbn.toLowerCase();
					Query query =qr.keyword().wildcard().onField("isbn").matching(standardIsbn).createQuery();
					booleanQueryIsbns.add(query, Occur.SHOULD);
				}
				booleanQuery.add(booleanQueryIsbns, Occur.MUST);
				return booleanQuery;
			}
		}
		/*if(StringUtils.isNotBlank(vo.getCreaterId())){
			boolQr.must(qr.keyword().onField("createUser.id").matching(vo.getCreaterId()).createQuery());
		}*/
		if (StringUtils.isNotBlank(vo.getTitle())) {
			boolQr.must(qr.keyword().onField("title").matching(vo.getTitle()).createQuery());
		}
		if (StringUtils.isNotBlank(vo.getSubTitle())) {
			boolQr.must(qr.keyword().onField("subTitle").matching(vo.getSubTitle()).createQuery());
		}
		if (StringUtils.isNotBlank(vo.getSeriesTitle())) {
			boolQr.must(qr.keyword().onField("seriesTitle").matching(vo.getSeriesTitle()).createQuery());
		}
		if (StringUtils.isNotBlank(vo.getChineseTitle())) {
			boolQr.must(qr.keyword().onField("chineseTitle").matching(vo.getChineseTitle()).createQuery());
		}
		if(StringUtils.isNotBlank(vo.getChineseSubjectHeadings())){
			boolQr.must(qr.keyword().onField("chineseSubjectHeadings").matching(vo.getChineseSubjectHeadings()).createQuery());
		}
		if(StringUtils.isNotBlank(vo.getSubjectHeadings())){
			boolQr.must(qr.keyword().onField("subjectHeadings").matching(vo.getSubjectHeadings()).createQuery());
		}	
		if (StringUtils.isNotBlank(vo.getPublisherName())) {
			String[] publisherNameArray=vo.getPublisherName().split(";");
			if(publisherNameArray.length==1){
				boolQr.must(qr.keyword().onField("publisherFullName").matching(vo.getPublisherName()).createQuery());
			}else{
				BooleanJunction<BooleanJunction> publisherNameBoolQr = qr.bool();
				for (int i = 0; i < publisherNameArray.length; i++) {
					publisherNameBoolQr.should(qr.keyword().onField("publisherFullName").matching(publisherNameArray[i]).createQuery());
				}
				boolQr.must(publisherNameBoolQr.createQuery());
			}
		}
		if (StringUtils.isNotBlank(vo.getCategory1())) {
			String[] category1Array=vo.getCategory1().split(";");
			if(category1Array.length==1){
				boolQr.must(qr.keyword().onField("category1").matching(vo.getCategory1()).createQuery());
			}else{
				BooleanJunction<BooleanJunction> category1BoolQr = qr.bool();
				for (int i = 0; i < category1Array.length; i++) {
					category1BoolQr.should(qr.keyword().onField("category1").matching(category1Array[i]).createQuery());
				}
				boolQr.must(category1BoolQr.createQuery());
			}
		}
		if (StringUtils.isNotBlank(vo.getCategory2())) {
			String[] category2Array=vo.getCategory2().split(";");
			if(category2Array.length==1){
				boolQr.must(qr.keyword().onField("category2").matching(vo.getCategory2()).createQuery());
			}else{
				BooleanJunction<BooleanJunction> category2BoolQr = qr.bool();
				for (int i = 0; i < category2Array.length; i++) {
					category2BoolQr.should(qr.keyword().onField("category2").matching(category2Array[i]).createQuery());
				}
				boolQr.must(category2BoolQr.createQuery());
			}
		}
		/*if(StringUtils.isNoneBlank(vo.getCheckStatus())){
			boolQr.must(qr.keyword().onField("checkStatus").matching(vo.getCheckStatus()).createQuery());
		}*/
		if (StringUtils.isNotBlank(vo.getAuthor())) {
			boolQr.must(qr.keyword().onField("author").matching(vo.getAuthor()).createQuery());
		}
		if (vo.getBinding() != null && vo.getBinding().size() > 0) {
			BooleanJunction<BooleanJunction> bindingBoolQr = qr.bool();
			for (String binding : vo.getBinding()) {
				if(binding!=null&&binding!="")
					bindingBoolQr.should(qr.keyword().onField("binding").matching(binding).createQuery());
			}
			boolQr.must(bindingBoolQr.createQuery());
		}
		if (vo.getClcs() != null && vo.getClcs().size() > 0) {
			BooleanJunction<BooleanJunction> clcBoolQr = qr.bool();
			for (String clc : vo.getClcs()) {
				clcBoolQr.should(qr.keyword().onField("clcs").matching(clc).createQuery());
				//clcBoolQr.should(new PrefixQuery(new Term("clcs.clc", clc.toLowerCase())));
			}
			boolQr.must(clcBoolQr.createQuery());
		}
		if (StringUtils.isNotBlank(vo.getContents())) {
			boolQr.must(qr.keyword().onField("contents").matching(vo.getContents()).createQuery());
		}
		if (vo.getCurrency() != null && vo.getCurrency().size() > 0) {
			BooleanJunction<BooleanJunction> currencyBoolQr = qr.bool();
			for (String currency : vo.getCurrency()) {
				currencyBoolQr.should(qr.keyword().onField("currency").matching(currency).createQuery());
			}
			boolQr.must(currencyBoolQr.createQuery());
		}
		if (StringUtils.isNotBlank(vo.getDescription())) {
			boolQr.must(qr.keyword().onField("description").matching(vo.getDescription()).createQuery());
		}
		if (StringUtils.isNotBlank(vo.getEdition())) {
			boolQr.must(qr.keyword().onField("edition").matching(vo.getDescription()).createQuery());
		}
		if (vo.getLanguages() != null && vo.getLanguages().size() > 0) {
			BooleanJunction<BooleanJunction> languageBoolQr = qr.bool();
			for (String language : vo.getLanguages()) {
				if(language!=null&&language!="")
					languageBoolQr.should(qr.keyword().onField("language").matching(language).createQuery());
			}
			boolQr.must(languageBoolQr.createQuery());
		}
		if (vo.getMinPageSize() != null || vo.getMaxPageSize() != null) {
			int minPageSize = vo.getMinPageSize() == null ? 0 : vo.getMinPageSize();
			int maxPageSize = vo.getMaxPageSize() == null ? Integer.MAX_VALUE : vo.getMaxPageSize();
			boolQr.must(qr.range().onField("pageSize").from(minPageSize).to(maxPageSize).createQuery());
		}
		if(vo.getMaxPrice()!=null&&vo.getMaxPrice()>0){
			if (vo.getMinPrice() != null || vo.getMaxPrice() != null) {
				float minPrice = vo.getMinPrice() == null ? 0 : vo.getMinPrice();
				float maxPrice = vo.getMaxPrice() == null ? Float.MAX_VALUE : vo.getMaxPrice();
				boolQr.must(qr.range().onField("price").from(minPrice).to(maxPrice).createQuery());
			}
		}
		
		if (vo.getMinPubDate() != null || vo.getMaxPubDate() != null) {
			
			Calendar c = Calendar.getInstance();
			c.set(Calendar.YEAR, 1900);
			c.set(Calendar.MONTH, 0);
			c.set(Calendar.DATE, 1);
			Date minPubDate = vo.getMinPubDate() == null ? c.getTime() : vo.getMinPubDate();
			Date maxPubDate = vo.getMaxPubDate() == null ? DateUtils.addYears(new Date(), 10) : vo.getMaxPubDate();
			//java.sql.Date 类型的字段在全文检索时时间会向前加上一天，所以参数赋值时要先减去一天
			minPubDate=DateHelper.getBeforeOfDate(minPubDate);
			maxPubDate=DateHelper.getBeforeOfDate(maxPubDate);
			boolQr.must(qr.range().onField("pubDate").from(minPubDate).to(maxPubDate).createQuery());
		}
		/*if (vo.getPublisherIds!= null && vo.getPublisherIds().size() > 0) {
			BooleanJunction<BooleanJunction> publisherBoolQr = qr.bool();
			for (String publisherId : vo.getPublisherIds()) {
				publisherBoolQr.should(qr.keyword().onField("publisher.id").matching(publisherId).createQuery());
			}
			boolQr.must(publisherBoolQr.createQuery());
		}*/
		if (StringUtils.isNotBlank(vo.getSubjectHeadings())) {
			boolQr.must(qr.keyword().onField("subjectHeadings").matching(vo.getSubjectHeadings()).createQuery());
		}
	/*	if (StringUtils.isNotBlank(vo.getChineseSubjectHeadings())) {
			boolQr.must(qr.keyword().onField("chineseSubjectHeadings.chineseSubjectHeading").matching(vo.getChineseSubjectHeadings()).createQuery());
		}*/
		if (StringUtils.isNotBlank(vo.getReadership())) {
			boolQr.must(qr.keyword().onField("readership").matching(vo.getReadership()).createQuery());
		}

		/*if (vo.getStock() != null) {
			boolQr.must(qr.range().onField("stock").below(vo.getStock()).createQuery());
		}*/
		/*if (vo.getSubjectIds() != null && vo.getSubjectIds().size() > 0) {
			BooleanJunction<BooleanJunction> subjectBoolQr = qr.bool();
			for (String subjectId : vo.getSubjectIds()) {
				subjectBoolQr.should(qr.keyword().onField("subjects.id").matching(subjectId).createQuery());
			}
			boolQr.must(subjectBoolQr.createQuery());
		}*/

		if (StringUtils.isNotBlank(vo.getVolumn())) {
			boolQr.must(qr.keyword().onField("volumn").matching(vo.getVolumn()).createQuery());
		}
		if(vo.getStockCount()!=null){
			if(vo.getRangType()!=null){
				if(vo.getRangType().equals(">="))
					boolQr.must(qr.range().onField("stockCount").above(vo.getStockCount()).createQuery());
				if(vo.getRangType().equals("<="))
					boolQr.must(qr.range().onField("stockCount").below(vo.getStockCount()).createQuery());
				if(vo.getRangType().equals("="))
					boolQr.must(qr.keyword().onField("stockCount").matching(vo.getStockCount()).createQuery());
			}
			else
				boolQr.must(qr.keyword().onField("stockCount").matching(vo.getStockCount()).createQuery());
		}
		Query query = boolQr.createQuery();
		return query;
	}

	
	public List<BookInfoStatisticsTJVo> groupingByQuery(BookInfoSearchNormalVO vo){
		Query query=getBookInfoByNormalSearchVOQuery(vo);
		Sort sort=new Sort(new SortField("pubDate",SortField.STRING,true));
		BookInfoStatisticsTJVo publisherVos=groupingByQuery(query, "publisherFullName", sort, BookInfo.class);
		BookInfoStatisticsTJVo category1Vos=groupingByQuery(query, "category1", sort, BookInfo.class);
		BookInfoStatisticsTJVo category2Vos=groupingByQuery(query, "category2", sort, BookInfo.class);
		BookInfoStatisticsTJVo pubDateVos=groupingByQuery(query, "pubYear", sort, BookInfo.class);
		List<BookInfoStatisticsTJVo> tjVos=new ArrayList<BookInfoStatisticsTJVo>();
		tjVos.add(publisherVos);
		tjVos.add(category1Vos);
		tjVos.add(category2Vos);
		tjVos.add(pubDateVos);
		return tjVos;
	}
	@SuppressWarnings("rawtypes")
	@Override
	public PageViewVO<BookInfo> getBookInfoByProps(Map<String, Object> mapMustProps, Map<String, Object> mapShouldProps, int pageIndex, int pageSize) {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();

		if (mapMustProps != null && mapMustProps.size() > 0) {
			for (String key : mapMustProps.keySet()) {
				boolQr.must(qr.keyword().onField(key).matching(mapMustProps.get(key)).createQuery());
			}
		}

		if (mapShouldProps != null && mapShouldProps.size() > 0) {
			for (String key : mapShouldProps.keySet()) {
				boolQr.should(qr.keyword().onField(key).matching(mapShouldProps.get(key)).createQuery());
			}
		}

		Query query = boolQr.createQuery();
		return executeQuery(query, pageIndex, pageSize, BookInfo.class);
	}

	

	@SuppressWarnings("rawtypes")
	@Override
	public PageViewVO<BookInfo> fullTextSearchByPrefixMatch(String field, String matchValue, int pageIndex, int pageSize) {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		//boolQr.must(qr.keyword().onField("isDel").matching(false).createQuery());
		//boolQr.must(qr.keyword().onField("isWaitForHumanCheckApprove").matching(false).createQuery());
		//boolQr.must(qr.keyword().onField("isBlackBook").matching(false).createQuery());
		if (StringUtils.isNotBlank(matchValue)) {
			boolQr.must(new PrefixQuery(new Term(field, matchValue)));
		}
		Query query = boolQr.createQuery();
		PageViewVO<BookInfo> pg = executeQuery(query, pageIndex, pageSize, BookInfo.class);
		Analyzer analyzer = getAnalyzerByClass(BookInfo.class);
		pg.setAdditionalPara(new Object[] { analyzer, query });
		return pg;
	}

	@SuppressWarnings("rawtypes")
	@Override
	public PageViewVO<BookInfo> fullTextSearchByWhildMatch(String field, String matchValue, int pageIndex, int pageSize) {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		/*boolQr.must(qr.keyword().onField("isDel").matching(false).createQuery());
		boolQr.must(qr.keyword().onField("isWaitForHumanCheckApprove").matching(false).createQuery());
		boolQr.must(qr.keyword().onField("isBlackBook").matching(false).createQuery());*/
		if (StringUtils.isNotBlank(matchValue)) {
			boolQr.must(qr.keyword().wildcard().onField(field).matching(matchValue).createQuery());
		}
		Query query = boolQr.createQuery();
		PageViewVO<BookInfo> pg = executeQuery(query, pageIndex, pageSize, BookInfo.class);
		Analyzer analyzer = getAnalyzerByClass(BookInfo.class);
		pg.setAdditionalPara(new Object[] { analyzer, query });
		return pg;
	}

	@SuppressWarnings("rawtypes")
	@Override
	public PageViewVO<BookInfo> fullTextSearchByAutoMatch(String field, String matchValue, int pageIndex, int pageSize) {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		/*boolQr.must(qr.keyword().onField("isDel").matching(false).createQuery());
		boolQr.must(qr.keyword().onField("isWaitForHumanCheckApprove").matching(false).createQuery());
		boolQr.must(qr.keyword().onField("isBlackBook").matching(false).createQuery());*/
		if (StringUtils.isNotBlank(matchValue)) {
			boolQr.must(qr.keyword().onField(field).matching(matchValue).createQuery());
		}
		Query query = boolQr.createQuery();
		PageViewVO<BookInfo> pg = executeQuery(query, pageIndex, pageSize, BookInfo.class);
		Analyzer analyzer = getAnalyzerByClass(BookInfo.class);
		pg.setAdditionalPara(new Object[] { analyzer, query });
		return pg;
	}

	@SuppressWarnings("rawtypes")
	private Query getQueryByBookInfoSearchWebVos(List<BookInfoSearchWebVO> vos) {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		/*boolQr.must(qr.keyword().onField("isDel").matching(false).createQuery());
		boolQr.must(qr.keyword().onField("isWaitForHumanCheckApprove").matching(false).createQuery());
		boolQr.must(qr.keyword().onField("isBlackBook").matching(false).createQuery());*/
		if (vos != null && vos.size() > 0) {
			for (BookInfoSearchWebVO bookInfoSearchWebVO : vos) {
				Query query = getQueryByBookInfSearchWebVO(bookInfoSearchWebVO);
				if (query != null) {
					boolQr.must(query);
				}
			}
		}
		Query query = boolQr.createQuery();
		return query;
	}

	@SuppressWarnings("rawtypes")
	private Query getQueryByBookInfSearchWebVO(BookInfoSearchWebVO vo) {
		boolean flag = false;
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		if (StringUtils.isNotBlank(vo.getIsbn())) {
			String standardIsbn = vo.getIsbn().toLowerCase();
			if (IsbnConvertUtils.validate(standardIsbn)) {
				standardIsbn = IsbnConvertUtils.convertTo13place(standardIsbn);
			}
			standardIsbn = standardIsbn.toLowerCase();
			boolQr.must(qr.keyword().wildcard().onField("isbn").matching(standardIsbn).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getTitle())) {
			boolQr.must(qr.keyword().onField("title").matching(vo.getTitle()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getSubTitle())) {
			boolQr.must(qr.keyword().onField("subTitle").matching(vo.getSubTitle()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getSeriesTitle())) {
			boolQr.must(qr.keyword().onField("seriesTitle").matching(vo.getSeriesTitle()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getChineseTitle())) {
			boolQr.must(qr.keyword().onField("chineseTitle").matching(vo.getChineseTitle()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getAuthor())) {
			boolQr.must(qr.keyword().onField("author").matching(vo.getAuthor()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getBinding())) {
			boolQr.must(qr.keyword().onField("binding").matching(vo.getBinding()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getClcs())) {
			boolQr.must(new PrefixQuery(new Term("clcs", vo.getClcs().toLowerCase())));
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getContents())) {
			boolQr.must(qr.keyword().onField("contents").matching(vo.getContents()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getCurrency())) {
			boolQr.must(qr.keyword().onField("currency").matching(vo.getCurrency()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getDescription())) {
			boolQr.must(qr.keyword().onField("description").matching(vo.getDescription()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getEdition())) {
			boolQr.must(qr.keyword().onField("edition").matching(vo.getDescription()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getLanguage())) {
			boolQr.must(qr.keyword().onField("language").matching(vo.getLanguage()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getMinPageSize()) || StringUtils.isNotBlank(vo.getMaxPageSize())) {
			int minPageSize = 0;
			if (StringUtils.isNotBlank(vo.getMinPageSize())) {
				try {
					minPageSize = Integer.parseInt(vo.getMinPageSize());
				} catch (Exception ex) {
				}
			}
			int maxPageSize = Integer.MAX_VALUE;
			if (StringUtils.isNotBlank(vo.getMaxPageSize())) {
				try {
					maxPageSize = Integer.parseInt(vo.getMaxPageSize());
				} catch (Exception ex) {
				}
			}
			boolQr.must(qr.range().onField("pageSize").from(minPageSize).to(maxPageSize).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getMinPrice()) || StringUtils.isNotBlank(vo.getMaxPrice())) {
			float minPrice = 0;
			if (StringUtils.isNotBlank(vo.getMinPrice())) {
				try {
					minPrice = Float.parseFloat(vo.getMinPrice());
				} catch (Exception e) {
				}
			}
			float maxPrice = Float.MAX_VALUE;
			if (StringUtils.isNotBlank(vo.getMaxPrice())) {
				try {
					maxPrice = Float.parseFloat(vo.getMaxPrice());
				} catch (Exception e) {
				}
			}
			boolQr.must(qr.range().onField("price").from(minPrice).to(maxPrice).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getMinPubDate()) || StringUtils.isNotBlank(vo.getMaxPubDate())) {

			Date minPubDate = minCalendar.getTime();
			if (StringUtils.isNotBlank(vo.getMinPubDate())) {
				try {
					minPubDate = sdf.parse(vo.getMinPubDate());
				} catch (java.text.ParseException e) {
					e.printStackTrace();
				}
			}

			Date maxPubDate = DateUtils.addYears(new Date(), 10);
			if (StringUtils.isNotBlank(vo.getMaxPubDate())) {
				try {
					maxPubDate = sdf.parse(vo.getMaxPubDate());
				} catch (java.text.ParseException e) {
					e.printStackTrace();
				}
			}

			boolQr.must(qr.range().onField("pubDate").from(minPubDate).to(maxPubDate).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getPublisherName())) {
			boolQr.must(qr.keyword().onField("publisher.name").matching(vo.getPublisherName()).createQuery());
			flag = true;
		}
		if (StringUtils.isNotBlank(vo.getSubjectHeadings())) {
			boolQr.must(qr.keyword().onField("subjectHeadings").matching(vo.getSubjectHeadings()).createQuery());
			flag = true;
		}
		/*if (StringUtils.isNotBlank(vo.getChineseSubjectHeadings())) {
			boolQr.must(qr.keyword().onField("chineseSubjectHeadings.chineseSubjectHeading").matching(vo.getChineseSubjectHeadings()).createQuery());
			flag = true;
		}*/
		if (StringUtils.isNotBlank(vo.getReadership())) {
			boolQr.must(qr.keyword().onField("readership").matching(vo.getReadership()).createQuery());
			flag = true;
		}

		if (vo.getStock() != null) {
			boolQr.must(qr.range().onField("stock").below(vo.getStock()).createQuery());
			flag = true;
		}
		/*if (StringUtils.isNotBlank(vo.getSubjectName())) {
			boolQr.must(qr.keyword().onField("subjects").matching(vo.getSubjectName()).createQuery());
			flag = true;
		}*/
		if (StringUtils.isNotBlank(vo.getCategoryName())) {
			boolQr.must(qr.keyword().onField("category1").matching(vo.getCategoryName()).createQuery());
			boolQr.must(qr.keyword().onField("category2").matching(vo.getCategoryName()).createQuery());
			flag = true;
		}

		if (StringUtils.isNotBlank(vo.getVolumn())) {
			boolQr.must(qr.keyword().onField("volumn").matching(vo.getVolumn()).createQuery());
			flag = true;
		}
		if (!flag)
			return null;
		return boolQr.createQuery();
	}

	@SuppressWarnings("rawtypes")
	@Override
	public List<String> fullTextSearchByNotTranslateChineseSubjectHeadingsIds() {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		//boolQr.must(qr.keyword().onField("isDel").matching(false).createQuery());
		Query query = boolQr.createQuery();
		List<Object> props = executeQueryAndProjectionProps(query, new String[] { "id" }, new String[] { "subjectHeadingEmpty" }, BookInfo.class);
		List<String> ids = new ArrayList<String>();
		if (props != null && props.size() > 0) {
			for (Object obj : props) {
				Object[] tempObjs = (Object[]) obj;
				String id = (String) tempObjs[0];
				ids.add(id);
			}
		}
		return ids;
	}
	
	
	public PageViewVO<BookInfo> getBookInfoByPublisherYear(int publisherYear, int pageIndex, int pageSize) {
		String hql=" from "+BookInfo.class.getName()+" t where t.pubYear=:publisherYear ";
		Map<String, Object> mapParams=new HashMap<String,Object>();
		mapParams.put("publisherYear", publisherYear);
		return getPageViewVO(hql, mapParams, pageIndex, pageSize);
	}

	@Override
	public BookInfo getUniqueBookByIsbn(String isbn) {
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		if (StringUtils.isNotBlank(isbn)) {
			String standardIsbn = isbn.toLowerCase();
			if (IsbnConvertUtils.validate(standardIsbn)) {
				standardIsbn = IsbnConvertUtils.convertTo13place(standardIsbn);
			}
			standardIsbn = standardIsbn.toLowerCase();
			boolQr.must(qr.keyword().wildcard().onField("isbn").matching(standardIsbn).createQuery());
		}
		Query query=boolQr.createQuery();
		
		return executeQueryForSingleObject(query, BookInfo.class);
	}
	
	public List<BookInfo> getFullTextBookInfoByIsbns(String[] isbns){
		if(isbns==null||isbns.length==0)
			return null;
		QueryBuilder qr = getQueryBuilder(BookInfo.class);
		BooleanQuery booleanQuery = new BooleanQuery();
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		if(isbns.length==1){
			String standardIsbn = isbns[0].toLowerCase();
			if (IsbnConvertUtils.validate(standardIsbn)) {
				standardIsbn = IsbnConvertUtils.convertTo13place(standardIsbn);
			}
			standardIsbn = standardIsbn.toLowerCase();
			boolQr.must(qr.keyword().wildcard().onField("isbn").matching(standardIsbn).createQuery());
		}else{
			BooleanJunction<BooleanJunction> isbnBoolQr = qr.bool();
			for (String isbn : isbns) {
				String standardIsbn = isbn.toLowerCase();
				if (IsbnConvertUtils.validate(standardIsbn)) {
					standardIsbn = IsbnConvertUtils.convertTo13place(standardIsbn);
				}
				standardIsbn = standardIsbn.toLowerCase();
				isbnBoolQr.should(qr.keyword().wildcard().onField("isbn").matching(standardIsbn).createQuery());
			}
			boolQr.must(isbnBoolQr.createQuery());
		}
		Query query=boolQr.createQuery();
		return executeQueryForListObject(query, BookInfo.class);
	}

	@Override
	public List<BookInfo> fullTextSearchBooksBySubscripeUserInfo(List<String> subjectIds, List<String> categoryIds,
			String subscripeUserId, int minBooksCount) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public BookInfoExtral getFullTextSearchBookInfoExtralByIsbn(String isbn){
		QueryBuilder qr = getQueryBuilder(BookInfoExtral.class);
		BooleanJunction<BooleanJunction> boolQr = qr.bool();
		if (StringUtils.isNotBlank(isbn)) {
			String standardIsbn = isbn.toLowerCase();
			if (IsbnConvertUtils.validate(standardIsbn)) {
				standardIsbn = IsbnConvertUtils.convertTo13place(standardIsbn);
			}
			standardIsbn = standardIsbn.toLowerCase();
			boolQr.must(qr.keyword().wildcard().onField("isbn").matching(standardIsbn).createQuery());
		}
		Query query = boolQr.createQuery();
		return executeQueryForSingleObject(query, BookInfoExtral.class);
	}
}
